/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			FreeMoveAnimator.d
	Description:	some basic animators
	Date:			25.11.2007 by Digited
*/
module moonglide.scenes.animators.FreeMoveAnimator;

import
	tango.math.Math,
	tango.text.convert.Float,

	moonglide.util.MoonUtil,

	moonglide.math.MoonMath,

	moonglide.io.LogSystem,
	moonglide.io.Config,

	moonglide.core.Events,
	moonglide.core.Controllers,
	moonglide.core.engobjects.EngObject;

//================================================================
/// struct with settings for freemove animator
//================================================================
struct SFMASettings
{
	Vector3f
		linearSpeeds, angularSpeeds,
		maxLinearSpeeds, maxAngularSpeeds,
		currentLinearAccels, currentAngularAccels,
		maxLinearAccels, maxAngularAccels,
		linearStoppingAccels, angularStoppingAccels,
//------------------------------------------------------------------------------
		targetOrient, targetmoving, targetPosition, targetRotation, targetRotate,
		moving, tmpVector;

	bool
		doLinearStoppingAccels, doAngularStoppingAccels, absoluteOrLocalAxis_StpAccels;

	float rotate_multiplifier, stop_time;

	static SFMASettings opCall()
	{
		SFMASettings settings;

		with( settings )
		{
			linearSpeeds = angularSpeeds = maxLinearSpeeds = maxAngularSpeeds = currentLinearAccels =
				currentAngularAccels = maxLinearAccels = maxAngularAccels = linearStoppingAccels =
				angularStoppingAccels = targetOrient = targetmoving = targetPosition = targetRotation =
				targetRotate = moving = tmpVector = Vector3f( _fzero_, _fzero_, _fzero_ );

				doLinearStoppingAccels = doAngularStoppingAccels = true;

				rotate_multiplifier = 1.f;
				stop_time = 0.f;
		}

		return settings;
	}

	static SFMASettings opCall( CConfig cfg )
	{
		SFMASettings settings = SFMASettings();

		settings.rotate_multiplifier = cfg.getFloat( "rotate_multiplifier", 1.0 );
		settings.stop_time = cfg.getFloat( "breaks_lag_time", 0.f );

		settings.maxLinearSpeeds.set( cfg.getFloat( "maxLinearSpeedsX", 3.5 ),
			cfg.getFloat( "maxLinearSpeedsY", 3.5 ), cfg.getFloat( "maxLinearSpeedsZ", 3.5 ) );

		settings.maxAngularSpeeds.set( cfg.getFloat( "maxAngularSpeedsX", 360.0 ),
			cfg.getFloat( "maxAngularSpeedsY", 360.0 ), cfg.getFloat( "maxAngularSpeedsZ", 360.0 ) );

		settings.maxLinearAccels.set( cfg.getFloat( "maxLinearAccelsX", 1.0 ),
			cfg.getFloat( "maxLinearAccelsY", 1.0 ), cfg.getFloat( "maxLinearAccelsZ", 1.0 ) );

		settings.maxAngularAccels.set( cfg.getFloat( "maxAngularAccelsX", 100.0 ),
			cfg.getFloat( "maxAngularAccelsY", 100.0 ), cfg.getFloat( "maxAngularAccelsZ", 100.0 ) );

		settings.linearStoppingAccels.set( cfg.getFloat( "linearStoppingAccelsX", 1.5 ),
			cfg.getFloat( "linearStoppingAccelsY", 1.5 ), cfg.getFloat( "linearStoppingAccelsZ", 1.5 ) );

		settings.angularStoppingAccels.set( cfg.getFloat( "angularStoppingAccelsX", 1.0 ),
			cfg.getFloat( "angularStoppingAccelsY", 1.0 ), cfg.getFloat( "angularStoppingAccelsZ", 1.0 ) );

		settings.doLinearStoppingAccels = cfg.getBool( "doLinearStoppingAccels", true );
		settings.doAngularStoppingAccels = cfg.getBool( "doAngularStoppingAccels", true );

		return settings;
	}
}


//================================================================
/// animator of free moving and orientation in 3d space.
//================================================================
class CFreeMoveAnimator : IAnimator
{
	mixin TEventGenerator;

	protected:
		uint 		xy1, xy2, xz1, xz2, yx1, yx2, yz1, yz2, zx1, zx2, zy1, zy2;	// matrix indexes for inv/uninv (camera/object)
		bool 		_inverted;

		CEngObject	_parent;
		CEvent		_event;

		//================================================================
		/// get accels data from controller states
		//================================================================
		void _getMoveData( float frametime )
		{
			static bool freezeState, lastFreezeState;

			freezeState = false;

//			if( mouseState.Keys[EMouseKeys._MB_RIGHT] )
//			{
//				freezeState = true;
//				_event.strings["state"] = "roll";
//
//				if( mouseState.x != 0 )
//					angularSpeeds.z = -mouseState.x * rotate_multiplifier * frametime;
//				else
//					angularSpeeds.z = _fzero_;
//			}
//			else
//				angularSpeeds.z = _fzero_;
//
//			// rolling disables yaw/pitch - you don't have to unhold lmb while rolling anymore
//			if( mouseState.Keys[EMouseKeys._MB_LEFT] && !mouseState.Keys[EMouseKeys._MB_RIGHT] )
//			{
//				freezeState = true;
//				_event.strings["state"] = "look";
//
//				if(( mouseState.x != 0 ) || ( mouseState.y != 0 ))
//				{
//					angularSpeeds.x = -mouseState.y * rotate_multiplifier * frametime;
//					angularSpeeds.y = -mouseState.x * rotate_multiplifier * frametime;
//				}
//				else
//					angularSpeeds.x = angularSpeeds.y = _fzero_;
//			}
//			else
//				angularSpeeds.x = angularSpeeds.y = _fzero_;

			if( keyboardState[ 83 ] )
				currentAngularAccels.x = 0.5f * frametime;
			else
				if( keyboardState[ 87 ] )
					currentAngularAccels.x = -0.5f * frametime;
				else
					currentAngularAccels.x = 0.0f;

			if( keyboardState[68] )
				currentAngularAccels.y = 0.5f * frametime;
			else
				if( keyboardState[65] )
					currentAngularAccels.y = -0.5f * frametime;
				else
					currentAngularAccels.y = 0.0f;

			if( keyboardState[81] )
				currentAngularAccels.z = 0.5f * frametime;
			else
				if( keyboardState[69] )
					currentAngularAccels.z = -0.5f * frametime;
				else
					currentAngularAccels.z = 0.0f;

			// check freeze mode
//			if( freezeState != lastFreezeState )
//			{
//				_event.bools["freeze"] = freezeState;
//
//				if( !freezeState )
//					_event.strings["state"] = "default";
//
//				lastFreezeState = freezeState;
//				generateEvent( _event );
//			}

		//---------------------------------

			if( keyboardState[40] )		// forward
				currentLinearAccels.z = maxLinearAccels.z * frametime;
			else
				if( keyboardState[38] )		// backward
					currentLinearAccels.z = -maxLinearAccels.z * frametime;
				else
					currentLinearAccels.z = _fzero_;

			if( keyboardState[39] )		// left
				currentLinearAccels.x = -maxLinearAccels.x * frametime;
			else
				if( keyboardState[37] )		// right
					currentLinearAccels.x = maxLinearAccels.x * frametime;
				else
					currentLinearAccels.x = _fzero_;

			if( keyboardState[_KB_HOME] )		// up
				currentLinearAccels.y = maxLinearAccels.y * frametime;
			else
				if( keyboardState[_KB_END] )		// down
					currentLinearAccels.y = -maxLinearAccels.y * frametime;
				else
					currentLinearAccels.y = _fzero_;
		}
//========================================================

	public:
		Vector3f
			linearSpeeds,
			angularSpeeds,

			maxLinearSpeeds,
			maxAngularSpeeds,

			currentLinearAccels,
			currentAngularAccels,

			maxLinearAccels,
			maxAngularAccels,

			linearStoppingAccels,
			angularStoppingAccels,

			linearStoppingTimers,
			angularStoppingTimers,
//------------------------------------------------------------------------------
			targetOrient,
			targetmoving,
			targetPosition,
			targetRotation,
			targetRotate;

		bool
			hasMoved,		// If the object has moved since last frame
			hasRotated,		// If the object has rotated since last frame

			doesTargetOrient,
			doesTargetmoving,
			doesTargetPosition,
			doesTargetRotation,
			doesTargetRotate,

			doLinearStoppingAccels,		// breaks y/n
			doAngularStoppingAccels;

		Vector3f
			moving,
			tmpVector;

		Matrix33f
			orientation,
			rotationMatrix;

		float rotate_multiplifier, stop_time;

		void inverted( bool inv )
		{
			if( _inverted != inv )
			{
				if( inv )
				{
					xy1 = 1; xy2 = 0;
					xz1 = 2; xz2 = 0;

					yx1 = 0; yx2 = 1;
					yz1 = 2; yz2 = 1;

					zx1 = 0; zx2 = 2;
					zy1 = 1; zy2 = 2;
				}
				else
				{
					xy1 = 0; xy2 = 1;
					xz1 = 0; xz2 = 2;

					yx1 = 1; yx2 = 0;
					yz1 = 1; yz2 = 2;

					zx1 = 2; zx2 = 0;
					zy1 = 2; zy2 = 1;
				}

				_inverted = inv;
			}
		}

		bool inverted()
		{
			return _inverted;
		}

		//================================================================
		/// subj
		//================================================================
		void recalclinearSpeeds()
		{
			tmpVector.set( orientation[0, 0], orientation[xy1, xy2], orientation[xz1, xz2] );
			linearSpeeds.x = dp( moving, tmpVector );

			tmpVector.set( orientation[yx1, yx2], orientation[1, 1], orientation[yz1, yz2] );
			linearSpeeds.y = dp( moving, tmpVector );

			tmpVector.set( -orientation[zx1, zx2], -orientation[zy1, zy2], -orientation[2, 2] );
			linearSpeeds.z = dp( moving, tmpVector );
		}

		//================================================================
		/// main processing
		//================================================================
		void animate( float time )
		{
//----------------------------------------------------------------------------------

				//================================================================
				/// brakes
				//================================================================
				void processStpAccel( inout float currentSpeed, float currentAccel,
					float stoppingAccel, float timeElapsed )
				{
					if(( currentAccel == _fzero_ ) && ( currentSpeed != _fzero_ ))
					{
						if( abs( currentSpeed ) > stoppingAccel * timeElapsed )
							currentSpeed += ( currentSpeed > _fzero_ )
								? -stoppingAccel * timeElapsed : stoppingAccel * timeElapsed;
						else
							currentSpeed = _fzero_;
					}
				}


				//============================================================
				/// unified stopping timer check - trying to avoid "stoppy" rotating & moving behaviour
				//============================================================
				bool checkTimer( inout float timer, float timeElapsed, bool wasmoving )
				{
					// if was moving last frame, start counting
					if( wasmoving )
						timer = stop_time;

					if( timer <= 0.f )
						return true;

					timer -= timeElapsed;

					if( timer <= 0.f )
					{
						timer = 0.f;
						return true;
					}

					return false;
				}

//----------------------------------------------------------------------------------

			moonAssert( time > 0, "time == 0", __FILE__, __LINE__ );

			_getMoveData( time ); // check mouse and kb state

			// 1. Apply accels, validate
			// 2. Apply breaks, if need
			// 3. Rotate
			// 4. Change "moving"
			// 5. If moving isn't zero, move.
			// 6. If rotated, update linear speeds using "moving:

			orientation = _parent.orientation;

//========== R O T A T I N G ===========================================

			if( _inverted )
				currentAngularAccels *= -1;

			// Apply accels -------------------------------
			if( currentAngularAccels.x != _fzero_ )
			{
				if( abs(currentAngularAccels.x) > maxAngularAccels.x )
					currentAngularAccels.x = (currentAngularAccels.x > 0)
						? maxAngularAccels.x : -maxAngularAccels.x;

				angularSpeeds.x += currentAngularAccels.x;
			}

			if( currentAngularAccels.y != _fzero_ )
			{
				if( abs(currentAngularAccels.y) > maxAngularAccels.y )
					currentAngularAccels.y = (currentAngularAccels.y > 0)
						? maxAngularAccels.y : -maxAngularAccels.y;

				angularSpeeds.y += currentAngularAccels.y;
			}

			if( currentAngularAccels.z != _fzero_ )
			{
				if( abs(currentAngularAccels.z) > maxAngularAccels.z )
					currentAngularAccels.z = (currentAngularAccels.z > 0)
						? maxAngularAccels.z : -maxAngularAccels.z;

				angularSpeeds.z += currentAngularAccels.z;
			}

			// Validate max speeds ----------------------------------
			if( abs(angularSpeeds.x) > maxAngularSpeeds.x )
				angularSpeeds.x = (angularSpeeds.x < _fzero_) ? -maxAngularSpeeds.x : maxAngularSpeeds.x;
			if( abs(angularSpeeds.y) > maxAngularSpeeds.y )
				angularSpeeds.y = (angularSpeeds.y < _fzero_) ? -maxAngularSpeeds.y : maxAngularSpeeds.y;
			if( abs(angularSpeeds.z) > maxAngularSpeeds.z )
				angularSpeeds.z = (angularSpeeds.z < _fzero_) ? -maxAngularSpeeds.z : maxAngularSpeeds.z;

			// Apply rotate breaks -------------------------------------------
			if( doAngularStoppingAccels )
			{
				static bool xmove, ymove, zmove;

				if( currentAngularAccels.x == _fzero_ )
				{
					if( checkTimer( angularStoppingTimers.y, time, xmove ))
						processStpAccel( angularSpeeds.x, currentAngularAccels.x, angularStoppingAccels.x, time );

					xmove = false;
				}
				else
					xmove = true;

				if( currentAngularAccels.y == _fzero_ )
				{
					if( checkTimer( angularStoppingTimers.y, time, ymove ))
						processStpAccel( angularSpeeds.y, currentAngularAccels.y, angularStoppingAccels.y, time );

					ymove = false;
				}
				else
					ymove = true;

				if( currentAngularAccels.z == _fzero_ )
				{
					if( checkTimer( angularStoppingTimers.y, time, zmove ))
						processStpAccel( angularSpeeds.z, currentAngularAccels.z, angularStoppingAccels.z, time );

					zmove = false;
				}
				else
					zmove = true;
			}

// Rotate --------------------------------------------------
			if( _inverted )
			{
				if( angularSpeeds.x != _fzero_ )
				{
					rotationMatrix = rotationMatrix * Matrix33f.rotationX( ToRadians( angularSpeeds.x ) );
					orientation = Matrix33f.identity * rotationMatrix;
				}

				if( angularSpeeds.y != _fzero_ )
				{
					rotationMatrix = rotationMatrix * Matrix33f.rotationY( ToRadians( angularSpeeds.y ) );
					orientation = Matrix33f.identity * rotationMatrix;
				}

				if( angularSpeeds.z != _fzero_ )
				{
					rotationMatrix = rotationMatrix * Matrix33f.rotationZ( ToRadians( angularSpeeds.z ) );
					orientation = Matrix33f.identity * rotationMatrix;
				}
			}
			else
			{
				if( angularSpeeds.x != _fzero_ )
					orientation = rotationMatrix = Matrix33f.rotationX( ToRadians( angularSpeeds.x )) * rotationMatrix;

				if( angularSpeeds.y != _fzero_ )
					orientation = rotationMatrix = Matrix33f.rotationY( ToRadians( angularSpeeds.y )) * rotationMatrix;

				if( angularSpeeds.z != _fzero_ )
					orientation = rotationMatrix = Matrix33f.rotationZ( ToRadians( angularSpeeds.z )) * rotationMatrix;
			}

			hasRotated =
				(( angularSpeeds.x != _fzero_ ) || ( angularSpeeds.y != _fzero_ ) || ( angularSpeeds.z != _fzero_ ));

			// If rotated, recalc linear speeds -------------
			if( hasRotated )
			{
				recalclinearSpeeds();
				_parent.orientation = orientation;
			}

//============ M O V I N G ==============================================

			moving.set( _fzero_, _fzero_, _fzero_ );

			if( _inverted )
				currentLinearAccels *= -1;

			if( currentLinearAccels.x != _fzero_ )
			{
				if( abs(currentLinearAccels.x) > maxLinearAccels.x )
					currentLinearAccels.x = (currentLinearAccels.x > 0)
						? maxLinearAccels.x : -maxLinearAccels.x;

				linearSpeeds.x += currentLinearAccels.x;
			}

			if( currentLinearAccels.y != _fzero_ )
			{
				if( abs(currentLinearAccels.y) > maxLinearAccels.y )
					currentLinearAccels.y = (currentLinearAccels.y > 0)
						? maxLinearAccels.y : -maxLinearAccels.y;

				linearSpeeds.y += currentLinearAccels.y;
			}

			if( currentLinearAccels.z != _fzero_ )
			{
				if( abs(currentLinearAccels.z) > maxLinearAccels.z )
					currentLinearAccels.z = (currentLinearAccels.z > 0)
						? maxLinearAccels.z : -maxLinearAccels.z;

				linearSpeeds.z += currentLinearAccels.z;
			}

			if( abs(linearSpeeds.x) > maxLinearSpeeds.x )
				linearSpeeds.x = (linearSpeeds.x < _fzero_) ? -maxLinearSpeeds.x : maxLinearSpeeds.x;
			if( abs(linearSpeeds.y) > maxLinearSpeeds.y )
				linearSpeeds.y = (linearSpeeds.y < _fzero_) ? -maxLinearSpeeds.y : maxLinearSpeeds.y;
			if( abs(linearSpeeds.z) > maxLinearSpeeds.z )
				linearSpeeds.z = (linearSpeeds.z < _fzero_) ? -maxLinearSpeeds.z : maxLinearSpeeds.z;

			// Apply move breaks -------------------------------------------
			if( doLinearStoppingAccels )
			{
				static bool xmove, ymove, zmove;

				if( currentLinearAccels.x == _fzero_ )
				{
					if( checkTimer( linearStoppingTimers.x, time, xmove ))
						processStpAccel( linearSpeeds.x, currentLinearAccels.x, linearStoppingAccels.x, time );

					xmove = false;
				}
				else
					xmove = true;

				if( currentLinearAccels.y == _fzero_ )
				{
					if( checkTimer( linearStoppingTimers.y, time, ymove ))
						processStpAccel( linearSpeeds.y, currentLinearAccels.y, linearStoppingAccels.y, time );

					ymove = false;
				}
				else
					ymove = true;

				if( currentLinearAccels.z == _fzero_ )
				{
					if( checkTimer( linearStoppingTimers.y, time, zmove ))
						processStpAccel( linearSpeeds.z, currentLinearAccels.z, linearStoppingAccels.z, time );

					zmove = false;
				}
				else
					zmove = true;
			}

			if( linearSpeeds.x != _fzero_ )
			{
				tmpVector.set( orientation[0, 0], orientation[xy1, xy2], orientation[xz1, xz2] );
				moving += tmpVector * linearSpeeds.x;
			}
			if( linearSpeeds.y != _fzero_ )
			{
				tmpVector.set( orientation[yx1, yx2], orientation[1, 1], orientation[yz1, yz2] );
				moving += tmpVector * linearSpeeds.y;
			}
			if( linearSpeeds.z != _fzero_ )
			{
				tmpVector.set( -orientation[zx1, zx2], -orientation[zy1, zy2], -orientation[2, 2] );
				moving += tmpVector * linearSpeeds.z;
			}

			hasMoved =
				(( linearSpeeds.x != _fzero_ ) || ( linearSpeeds.y != _fzero_ ) || ( linearSpeeds.z != _fzero_ ));

			if( hasMoved )
				_parent.position += moving;

			currentLinearAccels.set( _fzero_, _fzero_, _fzero_ );
			currentAngularAccels.set( _fzero_, _fzero_, _fzero_ );
		}

		//================================================================
		/// collision result
		//==c=============================================================
		void processForce( Vector3f Force )
		{
			//
		}

		//================================================================
		/// callback: getting todo-data
		//================================================================
		void onEventCallback( CEvent event, IEventGenerator gen )
		{
			//
		}

		void onEventReceive( CEvent event, IEventSender sender )
		{
			//
		}

		//================================================================
		/// other small stuff
		//================================================================
		void stopmoving( bool Immidiately = false )
		{
			if( Immidiately )
			{
				moving.set(_fzero_,_fzero_,_fzero_);
				linearSpeeds.set(_fzero_,_fzero_,_fzero_);
				currentLinearAccels.set(_fzero_,_fzero_,_fzero_);

				doesTargetmoving = doesTargetPosition = false;
			}
			else
			{
				setTargetmoving( Vector3f(_fzero_,_fzero_,_fzero_) );
			}
		}


		void stopRotating( bool Immidiately = false )
		{
			if( Immidiately )
			{
				angularSpeeds.set(_fzero_,_fzero_,_fzero_);
				currentAngularAccels.set(_fzero_,_fzero_,_fzero_);

				doesTargetRotation = doesTargetRotate = false;
			}
			else
			{
				setTargetRotation( Vector3f(_fzero_,_fzero_,_fzero_) );
			}
		}

//------------------------------------------------------------------------------------

		void setTargetOrient( Vector3f Orient )
		{
			doesTargetRotate = false;
			doesTargetRotation = false;

			doesTargetOrient = true;

			targetOrient = Orient;
		}

		bool hasTargetOrient( bool cleartarget = false ) { return doesTargetOrient; };

		void setTargetmoving( Vector3f moving )
		{
			doesTargetPosition = false;

			doesTargetmoving = true;

			targetmoving = moving;
		}

		bool hasTargetmoving( bool cleartarget = false ) { return doesTargetmoving; }

		void setTargetPosition( Vector3f Position )
		{
			doesTargetmoving = false;

			doesTargetPosition = true;

			targetPosition = Position;
		}

		bool hasTargetPosition( bool cleartarget = false ) { return doesTargetPosition; }

		void setTargetRotation( Vector3f Rotation )
		{
			doesTargetOrient = false;
			doesTargetRotate = false;

			doesTargetRotation = true;

			targetRotation = Rotation;
		}

		bool hasTargetRotation( bool cleartarget = false ) { return doesTargetRotation; }

		void setTargetRotate( Vector3f Rotate )
		{
			doesTargetOrient = false;
			doesTargetRotation = false;

			doesTargetRotate = true;

			targetRotate = Rotate;
		}

		bool hasTargetRotate( bool cleartarget = false ) { return doesTargetRotate; }

		//================================================================
		/// constructor with initializing
		//================================================================
		this( CEngObject obj, SFMASettings settings )
		{
			_parent = obj;

			_id = _parent.id ~ "_fmanim";

			linearSpeeds = settings.linearSpeeds; angularSpeeds = settings.angularSpeeds;
			maxLinearSpeeds = settings.maxLinearSpeeds; maxAngularSpeeds = settings.maxAngularSpeeds;
			currentLinearAccels =settings.currentLinearAccels; currentAngularAccels = settings.currentAngularAccels;
			maxLinearAccels = settings.maxLinearAccels; maxAngularAccels = settings.maxAngularAccels;
			linearStoppingAccels = settings.linearStoppingAccels; angularStoppingAccels = settings.angularStoppingAccels;
			targetOrient = settings.targetOrient; targetmoving = settings.targetmoving;
			targetPosition = settings.targetPosition; targetRotation = settings.targetRotation;
			targetRotate = settings.targetRotate;

			moving = tmpVector = Vector3f( _fzero_, _fzero_, _fzero_ );

			doLinearStoppingAccels = settings.doLinearStoppingAccels;
			doAngularStoppingAccels = settings.doAngularStoppingAccels;

			linearStoppingTimers = angularStoppingTimers = Vector3f( 0.f, 0.f, 0.f );

			rotate_multiplifier = settings.rotate_multiplifier;
			stop_time = settings.stop_time;

			orientation = obj.orientation;
			rotationMatrix = Matrix33f.identity;

			_event = new CEvent( eeventtype.program, eprogramevent.command );

			_inverted = true;	// --->
			inverted( false );	// setup indexes
		}

		~this()
		{
			_parent = null;
		}
}



//================================================================
/// animator of attached moving
//================================================================
class CAttachMoveAnimator : IAnimator
{
	mixin TEventGenerator;

	protected:
		float tmp;
		CEngObject _parent, _attachobj;

	public:
		void onEventCallback( CEvent event, IEventGenerator gen )
		{
			//
		}

		void onEventReceive( CEvent event, IEventSender sender )
		{
			//
		}

		void animate( float time )
		{
			_parent.wposition = _parent.position + _attachobj.wposition;
	//		_parent.worientation = _attachobj.worientation;
		}

		this( CEngObject obj, CEngObject attachobj )
		{
			_parent = obj;
			_attachobj = attachobj;
			_id = _parent.id ~ "_attanim";
		}

		~this()
		{
			_attachobj = _parent = null;
		}
}
